#ifndef _M_HTTPSERVER_HPP
#define _M_HTTPSERVER_HPP


#include "../server.hpp"
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <regex>
#include <cassert>

// 工具类，设置为静态函数，供用户调用即可
class Util
{
public:
    static size_t Split(const std::string &src, const std::string &sep, std::vector<std::string> *array)      // 字符串分割
    {
        std::string src_cp = src;
        std::string sep_cp = sep;
        char *pos = NULL;
        pos = strtok(&src_cp[0], &sep_cp[0]);   // 直接调cstring的 strtok
        while(pos != NULL)
        {
            array->push_back(pos);
            pos = strtok(NULL, &sep_cp[0]);
        }
        return array->size();
    }

    static bool ReadFile(const std::string &filename, std::string *buf)      // 读取文件所有内容放到 Buffer 中
    {
        std::ifstream ifs(filename, std::ios::binary);  // 二进制形式读取文件
        if(ifs.is_open() == false)
        {
            ERROR_LOG("open %s file failed !", filename);
            return false;
        }

        size_t filesize = 0;
        ifs.seekg(0, ifs.end);
        filesize = ifs.tellg();     // 获取文件大小
        ifs.seekg(0, ifs.beg);

        buf->resize(filesize);       // 开空间
        ifs.read(&(*buf)[0], filesize);    // 读取流正常
        if(ifs.good() == false)     // 检查流正常
        {
            ERROR_LOG("Read %s File Failed !", filename.c_str());
            ifs.close();
            return false;
        }
        ifs.close();
        return true;
    }

    static bool WriteFile(const std::string  &filename, const std::string &buf)      // 向文件写入
    {
        std::ofstream ofs(filename, std::ios::binary | std::ios::trunc);
        if(ofs.is_open() == false)
        {
            ERROR_LOG("Open %s File Failed !", filename.c_str());
            return false;
        }
        ofs.write(buf.c_str(), buf.size());
        if(ofs.good() == false)
        {
            ERROR_LOG("Write %s File Failed !", filename.c_str());
            ofs.close();
            return false;
        }
        ofs.close();
        return true;
    }

    // 对URL进行编码，避免URL中资源路径与查询字符串中的特殊字符与 请求中的特殊字符，产生歧义
    // 将 除 绝对不编码字符之外的字符，进行编码， 将其 ASCII 码值，转换为两个 16进制字符，前缀是 %，， ++ -》 %2B%2B
    // 不编码的字符：. - _ ~  和 字母、数字
    // 且W3C标准规定，查询字符串中的空格，需要编码为 + ，解码需要 + 转换为 空格
    static std::string UrlEncode(const std::string url, bool convert_space2plus)     // URL编码
    {
        std::string res;
        for(auto &c : url)
        {
            if(c == '.' || c == '-' || c == '_' || c == '~' || isalnum(c))      // isalnum 判断是否为字母和数字
            {
                res += c;
                continue;
            }
            if(c == ' ' && convert_space2plus)
            {
                res += '+';
                continue;
            }

            // 剩下字符都要编码
            char tmp[4] = {0};
            snprintf(tmp, sizeof(tmp), "%%%02X", c);
            res += tmp;
        }
        return res;
    }

    static char HexToI(char c)
    {
        if(c >= '0' && c <= '9')
        {
            return c - '0';
        }
        if(c >= 'a' && c <= 'z')
        {
            return c - 'a' + 10;
        }
        if(c >= 'A' && c <= 'Z')
        {
            return c - 'A' + 10;
        }
        return c;
    }

    static std::string UrlDecode(const std::string url, bool convert_plus2space)     // URL解码
    {
        std::string res;
        // 遇到 %，将其后的2个字符，转换为数字，第一个数字左移4位（等价于 *16），然后加上第二个数字
        for(int i = 0; i < url.size(); i++)
        {
            if(url[i] == '%' && (i + 2 < url.size()))
            {
                char v1 = HexToI(url[i + 1]);
                char v2 = HexToI(url[i + 2]);
                char v = (v1 << 4)  + v2;
                res += v;
                i+=2;
            }
            else if(url[i] == '+' && convert_plus2space)
            {
                res += ' ';
            }
            else
            {
                res += url[i];
            }
        }
        return res;
    }

    static std::string StatuDesc(int statu)      // 响应状态码的描述信息获取
    {
        std::unordered_map<int, std::string> _statu_msg = {
            {100, "Continue"},
            {101, "Switching Protocol"},
            {102, "Processing"},
            {103, "Early Hints"},
            {200, "OK"},
            {201, "Created"},
            {202, "Accepted"},
            {203, "Non-Authoritative Information"},
            {204, "No Content"},
            {205, "Reset Content"},
            {206, "Partial Content"},
            {207, "Multi-Status"},
            {208, "Already Reported"},
            {226, "IM Used"},
            {300, "Multiple Choice"},
            {301, "Moved Permanently"},
            {302, "Found"},
            {303, "See Other"},
            {304, "Not Modified"},
            {305, "Use Proxy"},
            {306, "unused"},
            {307, "Temporary Redirect"},
            {308, "Permanent Redirect"},
            {400, "Bad Request"},
            {401, "Unauthorized"},
            {402, "Payment Required"},
            {403, "Forbidden"},
            {404, "Not Found"},
            {405, "Method Not Allowed"},
            {406, "Not Acceptable"},
            {407, "Proxy Authentication Required"},
            {408, "Request Timeout"},
            {409, "Conflict"},
            {410, "Gone"},
            {411, "Length Required"},
            {412, "Precondition Failed"},
            {413, "Payload Too Large"},
            {414, "URI Too Long"},
            {415, "Unsupported Media Type"},
            {416, "Range Not Satisfiable"},
            {417, "Expectation Failed"},
            {418, "I'm a teapot"},
            {421, "Misdirected Request"},
            {422, "Unprocessable Entity"},
            {423, "Locked"},
            {424, "Failed Dependency"},
            {425, "Too Early"},
            {426, "Upgrade Required"},
            {428, "Precondition Required"},
            {429, "Too Many Requests"},
            {431, "Request Header Fields Too Large"},
            {451, "Unavailable For Legal Reasons"},
            {500, "Internal Server Error"},
            {501, "Not Implemented"},
            {502, "Bad Gateway"},
            {503, "Service Unavailable"},
            {504, "Gateway Timeout"},
            {505, "HTTP Version Not Supported"},
            {506, "Variant Also Negotiates"},
            {507, "Insufficient Storage"},
            {508, "Loop Detected"},
            {510, "Not Extended"},
            {511, "Network Authentication Required"}
        };

        auto it = _statu_msg.find(statu);
        if(it != _statu_msg.end())
        {
            return it->second;
        }
        return "Unknown statu !!!";
    }

    static std::string ExtMime(const std::string &filename)        // 根据文件后缀名获取文件 mime
    {
        std::unordered_map<std::string, std::string> _mime_msg = {
            {".html", "text/html"},
            {".htm", "text/html"},
            {".css", "text/css"},
            {".js", "application/javascript"},
            {".txt", "text/plain"},
            {".xml", "application/xml"},
            {".json", "application/json"},
            {".xht", "application/xhtml+xml"},
            {".xhtml", "application/xhtml+xml"},
            {".jpg", "image/jpeg"},
            {".jpeg", "image/jpeg"},
            {".png", "image/png"},
            {".gif", "image/gif"},
            {".bmp", "image/bmp"},
            {".webp", "image/webp"},
            {".svg", "image/svg+xml"},
            {".tif", "image/tiff"},
            {".tiff", "image/tiff"},
            {".mp3", "audio/mpeg"},
            {".wav", "audio/wav"},
            {".weba", "audio/webm"},
            {".webm", "video/webm"},
            {".mp4", "video/mp4"},
            {".avi", "video/x-msvideo"},
            {".3gp", "video/3gpp"},
            {".3g2", "video/3gpp2"},
            {".pdf", "application/pdf"},
            {".doc", "application/msword"},
            {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
            {".xls", "application/vnd.ms-excel"},
            {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
            {".ppt", "application/vnd.ms-powerpoint"},
            {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
            {".zip", "application/zip"},
            {".rar", "application/x-rar-compressed"},
            {".7z", "application/x-7z-compressed"},
            {".tar", "application/x-tar"},
            {".ttf", "font/ttf"},
            {".woff", "font/woff"},
            {".woff2", "font/woff2"},
            {".swf", "application/x-shockwave-flash"},
            {".vsd", "application/vnd.visio"},
            {".xul", "application/vnd.mozilla.xul+xml"},
            {".svg", "image/svg+xml"},
            {".swf", "application/x-shockwave-flash"},
            {".tar", "application/x-tar"},
            {".vsd", "application/vnd.visio"},
            {".weba", "audio/webm"},
            {".webm", "video/webm"},
            {".woff", "font/woff"},
            {".woff2", "font/woff2"},
            {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
            {".xul", "application/vnd.mozilla.xul+xml"}
        };

        // abc.txt
        size_t pos = filename.find_last_of('.');
        if(pos == std::string::npos)
        {
            // 没有后缀
            return "application/octet-stream";  // 二进制
        }

        std::string ext = filename.substr(pos); // 文件后缀
        auto it = _mime_msg.find(ext);
        if(it != _mime_msg.end())
        {
            return it->second;
        }
        return "application/octet-stream";  // 二进制
    }

    static bool IsDir(const std::string &filename)     // 判断文件是否为目录
    {
        struct stat st;
        int ret = stat(filename.c_str(), &st);      // 获取文件属性
        if(ret < 0)
        {
            ERROR_LOG("Util::static bool IsDir  Failed !");
            return false;
        }
        return S_ISDIR(st.st_mode);     // 直接用 stat 中的宏就可以
    }

    static bool IsRegular(const std::string &filename)     // 判断是否为普通文件
    {
        struct stat st;
        int ret = stat(filename.c_str(), &st);      // 获取文件属性
        if(ret < 0)
        {
            // ERROR_LOG("Util::static bool IsRegular  Failed !");
            return false;
        }
        return S_ISREG(st.st_mode);     // 直接用 stat 中的宏就可以
    }

    // http 请求的资源路径有效性判断
    //  /index.html
    //  避免 /../index.html 这种请求格式
    //  思想：将路径按照 / 分割为子目录，计算目录深度，遇到 ".." 目录深度 -1，整体目录深度不能小于0
    static bool ValidPath(const std::string &path)     // 请求的资源路径的有效性判断
    {
        if(path == "/") return true;
        std::vector<std::string> subdir;
        Split(path, "/", &subdir);

        // for(auto &dir : subdir)
        // {
        //     std::cout << "[" << dir << "]" << " ";
        // }
        std::cout << std::endl;

        int level = 0;      // 初始目录深度
        for(auto &d : subdir)
        {
            if(d == "..")
            {
                level--;
                if(level < 0)
                {
                    return false;
                }
            }
            else
            {
                level++;
            }
        }
        return level > 0;
    }

};


class HttpRequest
{
public:
    std::string _method;        // 请求方法
    std::string _path;          // 资源路径
    std::string _version;       // 协议版本
    std::string _body;          // 请求正文
    std::smatch _matches;       // 资源路径的 正则 提取数据
    std::unordered_map<std::string, std::string> _headers;      // 头部字段
    std::unordered_map<std::string, std::string> _params;      // 查询字符串

public:
    HttpRequest()
        :_version("HTTP/1.1")
    {}

    void ReSet()        // 以防处理请求的 上下文，对下次处理造成影响 
    {
        _method.clear();
        _path.clear();
        _version = "HTTP/1.1";
        _body.clear();
        std::smatch match;
        _matches.swap(match);
        _headers.clear();
        _params.clear();
    }

    void SetHeader(const std::string &key, const std::string &val)      // 插入头部字段
    {
        _headers.insert(std::make_pair(key, val));
    }

    bool HasHeader(const std::string &key) const      // 判断是否存在 某个头部字段
    {
        auto it = _headers.find(key);
        if(it == _headers.end())
        {
            return false;
        }
        return true;
    }

    std::string GetHeader(const std::string &key) const     // 获取某个头部字段的 值
    {
        if(HasHeader(key))
        {
            auto it = _headers.find(key);
            if(it == _headers.end())
            {
                return "";
            }
            return it->second;
        }
        return "";
    }

    void SetParam(const std::string &key, const std::string &val)      // 插入查询字符串
    {
        _params.insert(std::make_pair(key, val));
    }

    bool HasParam(const std::string &key)      // 判断是否存在 某个查询字符串
    {
        auto it = _params.find(key);
        if(it == _params.end())
        {
            return false;
        }
        return true;
    }

    std::string GetParam(const std::string &key)     // 获取某个查询字符串的 值
    {
        if(HasParam(key))
        {
            auto it = _params.find(key);
            if(it == _params.end())
            {
                return "";
            }
            return it->second;
        }
        return "";
    }

    size_t ContentLength()      // 获取正文长度
    {
        bool ret = HasHeader("Content-Length");
        if(ret == false)
        {
            return 0;
        }
        std::string contlen = GetHeader("Content-Length");
        return std::stol(contlen);
    }

    bool Close() const        // 是否短链接？
    {
        // 没有 Connection 字段，或者有 Connection 字段，但是值是 close，就都是短连接；； keep-alive 就是 长连接
        if(HasHeader("Connection") && GetHeader("Connection") == "keep-alive")
        {
            return false;
        }
        return true;
    }
    

};



class HttpResponse
{
public:
    int _statu;     // 响应状态码
    bool _redirect_flag;        // 重定向标志
    std::string _body;      // 响应正文
    std::string _redirect_url;      // 重定向字段
    std::unordered_map<std::string, std::string> _headers;      // 头部字段

public:
    HttpResponse()
        :_redirect_flag(false), _statu(200)
    {}
    HttpResponse(int statu)
        :_redirect_flag(false), _statu(statu)
    {}

    void Reset()
    {
        _statu = 200;
        _redirect_flag = false;
        _body.clear();
        _redirect_url.clear();
        _headers.clear();
    }

    void SetHeader(const std::string &key, const std::string &val)      // 插入头部字段
    {
        _headers.insert(std::make_pair(key, val));
    }

    bool HasHeader(const std::string &key)      // 判断是否存在 某个头部字段
    {
        auto it = _headers.find(key);
        if(it == _headers.end())
        {
            return false;
        }
        return true;
    }

    std::string GetHeader(const std::string &key)     // 获取某个头部字段的 值
    {
        if(HasHeader(key))
        {
            auto it = _headers.find(key);
            if(it == _headers.end())
            {
                return "";
            }
            return it->second;
        }
        return "";
    }

    void SetContent(const std::string &body, const std::string &type)
    {
        _body = body;
        SetHeader("Content-Type", type);
    }

    void SetRedirect(const std::string &url, int statu = 302)         // 注意，这里并没有把重定向字段设置进 响应报头
    {
        _statu = statu;
        _redirect_flag = true;
        _redirect_url = url;
    }

    bool Close()        // 是否短链接？
    {
        // 没有 Connection 字段，或者有 Connection 字段，但是值是 close，就都是短连接；； keep-alive 就是 长连接
        if(HasHeader("Connection") && GetHeader("Connection") == "keep-alive")
        {
            return false;
        }
        return true;
    }

};


#define MAX_LINE 8192       // 头部长度最大 8kb

typedef enum{
    RECV_HTTP_ERROR,
    RECV_HTTP_LINE,
    RECV_HTTP_HEAD,
    RECV_HTTP_BODY,
    RECV_HTTP_OVER
}HttpRecvStatu;

// 记录 Http 请求的接收及处理进度
class HttpContext
{
private:
    int _resp_statu;        // 响应状态码
    HttpRecvStatu _recv_statu;      // 当前接收和解析的阶段到哪里了
    HttpRequest _request;       // 存放当前已经解析后得到的请求信息
private:
    
    // 解析请求行
    bool ParseHttpLine(const std::string &line)
    {
        std::smatch matches;
        std::regex e("(GET|POST|HEAD|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase);  // 标志位可以忽略大小写进行匹配
        bool ret = std::regex_match(line, matches, e);
        if(!ret)
        {
            ERROR_LOG("HttpContext::ParseHttpLine, regex_match Failed !");
            _recv_statu = RECV_HTTP_ERROR;
            _resp_statu = 400;  // BAD REQUEST
            return false;
        }

        /*
        0: GET http://c.pki.goog/r/gsr1.crl?user=zhangsan&pass=12325 HTTP/1.1\r\n
        1: GET
        2: http://c.pki.goog/r/gsr1.crl
        3: user=zhangsan&pass=12325
        4: HTTP/1.1
        */
        _request._method = matches[1];  // 请求方法
        std::transform(_request._method.begin(), _request._method.end(), _request._method.begin(), ::toupper);  // 转大写
        _request._path = Util::UrlDecode(matches[2], false);        // 接收的请求中的 URL 是编码过的，需要解码
        _request._version = matches[4]; // 协议版本
        
        // 对多个查询字符串的键值对进行解析
        std::vector<std::string> query_string_array;
        std::string query_string = matches[3];
        Util::Split(query_string, "&", &query_string_array);
        
        for(auto &query : query_string_array)   // 对单个 key=val 的查询字符串进行解析
        {
            std::vector<std::string> single_query_split;
            Util::Split(query, "=", &single_query_split);
            if(single_query_split.size() <= 1)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _resp_statu = 400;  // BAD REQUEST
                return false;
            }
            // 因为查询字符串中，编码时 空格会转加号，所以解码时需要加号转空格
            std::string key = Util::UrlDecode(single_query_split[0], true);
            std::string val = Util::UrlDecode(single_query_split[1], true);
            _request.SetParam(key, val);        // 将该查询字符串设置进请求的信息
        }

        return true;
    }


    // 接收并解析请求行
    bool RecvHttpLine(Buffer *buf)      
    {
        if(_recv_statu != RECV_HTTP_LINE) return false;
        std::string line = buf->GetLine();  // 获取第一行
        if(line.size() == 0)
        {
            // 如果缓冲区数据 不足一行，分两种情况
            if(buf->ReadableSize() > MAX_LINE)
            {
                // 如果一行数据超大，则需要判断缓冲区的可读数据长度，如果很长了都不足一行，肯定出问题了
                _recv_statu = RECV_HTTP_LINE;
                _resp_statu = 414;  // URL TOO LONG
                return false;
            }
            
            return true;    // 缓冲区的数据不足一行，但是不大；；那就需要继续等待新数据
        }

        // 成功获取到第一行
        if(line.size() > MAX_LINE)      // 第一行不能太大
        {
            _recv_statu = RECV_HTTP_LINE;
            _resp_statu = 414;  // URL TOO LONG
            return false;
        }

        

        // 请求行接收完毕，解析：
        bool ret = ParseHttpLine(line);
        if(ret == false)
        {
            ERROR_LOG("HttpContext::RecvHttpLine, ParseHttpLine Failed !");
            return false;
        }
        
        _recv_statu = RECV_HTTP_HEAD;   // 请求行已处理完毕，进入Head获取并处理
        return true;
    }

    // key: val\r\n
    // key: val\n
    // 接收头部字段，逐行取出数据解析，直到遇到空行为止
    bool RecvHttpHead(Buffer *buf)
    {
        if(_recv_statu != RECV_HTTP_HEAD) return false;
        while(1)
        {
            std::string line = buf->GetLine();  // 获取第一行
            if(line.size() == 0)
            {
                // 如果缓冲区数据 不足一行，分两种情况
                if(buf->ReadableSize() > MAX_LINE)
                {
                    // 如果一行数据超大，则需要判断缓冲区的可读数据长度，如果很长了都不足一行，肯定出问题了
                    _recv_statu = RECV_HTTP_LINE;
                    _resp_statu = 414;  // URL TOO LONG
                    return false;
                }
                
                return true;    // 缓冲区的数据不足一行，但是不大；；那就需要继续等待新数据
            }
    
            // 成功获取到第一行
            if(line.size() > MAX_LINE)      // 第一行不能太大
            {
                _recv_statu = RECV_HTTP_LINE;
                _resp_statu = 414;  // URL TOO LONG
                return false;
            }
    
            // 对接收的这一行，判断是否为空行；遇到空行，直接退出，因为 Buffer 会自动移动可读位置，所以 Buffer 中不会残留这个空行
            if(line == "\r\n" || line == "\n")
            {
                break;
            }
    
            // 取完一行了，进行解析
            bool ret = ParseHttpHead(line);
            if(ret == false)
            {
                ERROR_LOG("HttpContext::RecvHttpHead, ParseHttpHead Failed !");
                return false;
            }
        }
        _recv_statu = RECV_HTTP_BODY;   // 头部已处理完毕，进入正文获取并处理
        return true;
    }

    // bool ParseHttpHead(const std::string &const_line)
    bool ParseHttpHead(std::string &line)
    {
        // std::string line = const_line;
        // key: val\r\n
        // key: val\n
        if(line.back() == '\n') line.pop_back();
        if(line.back() == '\r') line.pop_back();
        std::vector<std::string> single_query_split;        // 复用上面解析单个查询字符串键值对的代码
        Util::Split(line, ": ", &single_query_split);
        if(single_query_split.size() <= 1)
        {
            _recv_statu = RECV_HTTP_ERROR;
            _resp_statu = 400;  // BAD REQUEST
            return false;
        }

        std::string key = single_query_split[0];
        std::string val = single_query_split[1];
        _request.SetHeader(key, val);        // 将该头部键值对设置进请求的头部信息
        return true;
    }

    // 接收正文
    bool RecvHttpBody(Buffer *buf)
    {
        if(_recv_statu != RECV_HTTP_BODY) return false;
        // 获取正文长度
        size_t content_length = _request.ContentLength();
        if(content_length == 0)
        {
            // 没有正文，接收完毕
            _recv_statu = RECV_HTTP_OVER;
            return true;
        }

        // 当前已经接受了多少正文？，因为正文可能分多次接收
        size_t need_len = content_length - _request._body.size();
        if(need_len == 0)
        {
            // 接收完了
            _recv_statu = RECV_HTTP_OVER;
            return true;
        }

        // 接收并放入body，考虑当前缓冲区中的数据，剩下还有多少没接收就从缓冲区中取多少，因为 请求可能是一条接一条的
        // 1、缓冲区数据大于还没取的正文长度
        if(buf->ReadableSize() >= need_len)
        {
            _request._body.append(buf->ReadPos(), need_len);     // 取剩余的正文，追加到body
            buf->ReadMove(need_len);
            _recv_statu = RECV_HTTP_OVER;
            return true;
        }

        // 2、缓冲区数据小于还没取的正文长度，等待新数据的到来
        if(buf->ReadableSize() < need_len)
        {
            _request._body.append(buf->ReadPos(), buf->ReadableSize());     // 把缓冲区的所有数据都取出来
            buf->ReadMove(buf->ReadableSize());
            return true;
        }

        return false;
    }

public:
    HttpContext()
        :_resp_statu(200), _recv_statu(RECV_HTTP_LINE)
    {}

    void ReSet()
    {
        _resp_statu = 200;
        _recv_statu = RECV_HTTP_LINE;
        _request.ReSet();
    }


    int RespStatu()     // 获取响应状态码
    {
        return _resp_statu;
    }

    HttpRecvStatu RecvStatu()       // 获取当前接收和解析状态
    {
        return _recv_statu;
    }

    HttpRequest &Request()      // 获取当前解析好的请求信息
    {
        return _request;
    }

    void RecvHttpRequest(Buffer *buf)      // 接收，解析Http请求，整合以上部分接口
    {
        switch(_recv_statu)
        {
            case RECV_HTTP_LINE:
                RecvHttpLine(buf);  // 无需break，处理完LINE、接着处理 HEAD、BODY
            case RECV_HTTP_HEAD:
                RecvHttpHead(buf);
            case RECV_HTTP_BODY:
                RecvHttpBody(buf);
        }
        return;
    }
};



#define DEFALT_TIMEOUT 10

class HttpServer
{
private:
    using Handler = std::function<void(const HttpRequest &, HttpResponse *)>;
    // 各请求方法的路由表：存储键值对
    // using Handlers = std::unordered_map<std::string, Handler>;      // key 是正则表达式，，val 是处理函数
    using Handlers = std::vector<std::pair<std::regex, Handler>>;      // key 是正则表达式，，val 是处理函数
    // 使用正则表达式，对请求的资源路径进行正则匹配，匹配成功就使用对应函数进行处理
    Handlers _get_route;
    Handlers _post_route;
    Handlers _put_route;
    Handlers _delete_route;
    std::string _basedir;   // 静态资源的根目录
    TcpServer _server;

private:
    // 将 HttpResponse 中的结构化成员，组织为http协议格式进行发送
    void WriteResponse(const PtrConnection &conn, const HttpRequest &req, HttpResponse *rsp)    
    {
        // 1、先完善头部字段 
        if (req.Close())
        {
            rsp->SetHeader("Connection", "close");
        }
        else
        {
            rsp->SetHeader("Connection", "keep-alive");
        }

        if(!rsp->_body.empty() && !rsp->HasHeader("Content-Length"))        // 如果响应的正文非空，且没有 Content-Length 字段
        {
            rsp->SetHeader("Content-Length", std::to_string(rsp->_body.size()));    // 进行设置
        }

        if(!rsp->_body.empty() && !rsp->HasHeader("Content-Type"))        // 如果响应的正文非空，且没有 Content-Type 字段
        {
            rsp->SetHeader("Content-Type", "application/octet-stream");    // 进行设置，正文是 二进制流 格式
        }

        if(rsp->_redirect_flag)     // 如果响应设置了重定向
        {
            rsp->SetHeader("Location", rsp->_redirect_url);
        }

        // 2、将 rsp 中的结构化成员，按照 http 协议格式进行组织
        std::stringstream rsp_str;
        rsp_str << req._version << " " << std::to_string(rsp->_statu) << " " << Util::StatuDesc(rsp->_statu) << "\r\n";
        for(auto &head : rsp->_headers)
        {
            rsp_str << head.first << ": " << head.second << "\r\n";
        }
        rsp_str << "\r\n";
        rsp_str << rsp->_body;

        // 3、发送数据
        conn->Send(rsp_str.str().c_str(), rsp_str.str().size());
    }

    bool IsFileHandler(HttpRequest &req)
    {
        // 判断是否静态资源请求
        // 1、必须已经设置了静态资源根目录
        if(_basedir.empty())
        {
            // DEBUG_LOG("IsFileHandler: 静态资源根目录未设置（_basedir为空）");
            return false;
        }

        // 2、必须是 GET、HEAD 方法
        if(req._method != "GET" && req._method != "HEAD")
        {
            // DEBUG_LOG("IsFileHandler: 非GET/HEAD方法（当前方法：%s），不处理静态资源", req._method.c_str());
            return false;
        }

        // 3、请求资源路径必须合法
        if(!Util::ValidPath(req._path))
        {
            // DEBUG_LOG("IsFileHandler: 请求路径不合法（路径：%s），可能包含恶意跳转（如../）", req._path.c_str());
            return false;
        }

        // 4、请求资源必须要存在
        std::string req_path = _basedir + req._path;        // 先用局部变量，先不修改req._path
        if(req._path.back() == '/')     // 是个目录？让默认访问主页
        {
            req_path += "index.html";
            // DEBUG_LOG("IsFileHandler: 目录请求，自动补充index.html，实际路径：%s", req_path.c_str());
        }
        else
        {
            // DEBUG_LOG("IsFileHandler: 构建实际路径：%s（_basedir：%s + 请求路径：%s）", 
                // req_path.c_str(), _basedir.c_str(), req._path.c_str());
        }

        if(!Util::IsRegular(req_path))
        {
            // DEBUG_LOG("IsFileHandler: 实际路径不是有效文件（路径：%s），可能文件不存在或不是常规文件", req_path.c_str());
            // 请求的不是静态资源
            return false;
        }
        
        // req._path = req_path;   // 就是静态请求，修改资源路径

        // DEBUG_LOG("IsFileHandler: 验证通过，是静态资源请求（实际路径：%s）", req_path.c_str());
        return true;
    }

    bool FileHandler(const HttpRequest &req, HttpResponse *rsp)      // 静态资源的请求处理
    {
        std::string req_path = _basedir + req._path;        // 先用局部变量，先不修改req._path
        if(req._path.back() == '/')     // 是个目录？让默认访问主页
        {
            if (!_basedir.empty() && _basedir.back() == '/') {
                // 若 _basedir 以 / 结尾，去掉 req._path 开头的 /
                req_path = _basedir + (req._path.empty() ? "" : req._path.substr(1));
            }
            req_path += "index.html";
            // DEBUG_LOG("FileHandler: 目录请求，自动补充index.html，实际路径：%s", req_path.c_str());
        }
        else
        {
            // DEBUG_LOG("FileHandler: 构建实际路径：%s（_basedir：%s + 请求路径：%s）", 
                // req_path.c_str(), _basedir.c_str(), req._path.c_str());
        }

        // 将静态资源文件的数据读出来，放进 rsp 的 body 中，并设置 mime
        bool ret = Util::ReadFile(req_path, &rsp->_body);
        if(!ret)
        {
            // ERROR_LOG("FileHandler: 读取文件失败（路径：%s），可能文件不存在或无读取权限", req_path.c_str());
            return false;
        }
        // DEBUG_LOG("FileHandler: 读取文件成功（路径：%s，大小：%zu字节）", req_path.c_str(), rsp->_body.size());
        
        // 提取mime，并设置
        std::string mime = Util::ExtMime(req_path);
        rsp->SetHeader("Content-Type",  mime);
        // DEBUG_LOG("FileHandler: 设置Content-Type为：%s（文件路径：%s）", mime.c_str(), req_path.c_str());
        return true;
    }

    void Dispatcher(HttpRequest &req, HttpResponse *rsp, Handlers &route)       // 功能性请求的分类处理
    {
        // 在路由表中查找是否含有 请求方法的处理函数，有则调用，没有返回 404
        for(auto &r : route)
        {
            const std::regex &re = r.first;     // 路由表的key  但是这里每次循环都要进行编译正则表达式，需要改进
            Handler &func = r.second;   // 路由表的val
            bool ret = std::regex_match(req._path, req._matches, re);   // 将请求的资源路径，逐个与此 路由表中的key匹配，匹配成功则调用对应函数，全部失败则返回404
            if(ret)
            {
                return func(req, rsp);  // 匹配成功，直接调用
            }
        }
        // 全部匹配失败
        rsp->_statu = 404;
    }

    void Route(HttpRequest &req, HttpResponse *rsp)        // 在路由表中进行查找
    {
        // 1、对请求进行分辨，判断是一个静态资源请求，还是一个功能性请求
        // GET、HEAD 都先默认是 静态资源请求
        // 如果既不是静态资源请求，也没有设置对应的功能性请求函数，就返回 405
        if(IsFileHandler(req))
        {
            FileHandler(req, rsp);   // 是静态资源请求
            return;
        }
        if(req._method == "GET" || req._method == "HEAD")
        {
            return Dispatcher(req, rsp, _get_route);
        }
        else if(req._method == "POST")
        {
            return Dispatcher(req, rsp, _post_route);
        }
        else if(req._method == "PUT")
        {
            return Dispatcher(req, rsp, _put_route);
        }
        else if(req._method == "DELETE")
        {
            return Dispatcher(req, rsp, _delete_route);
        }

        rsp->_statu = 405;  // Method Not Allowed
    }

    void ErrorHandler(const HttpRequest &req, HttpResponse *rsp)
    {
        // 组织一个错误展示页面
        std::string body;
        body += "<html>";
        body += "<head>";
        body += "<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>";
        body += "</head>";
        body += "<body>";
        body += "<h1>";
        body += std::to_string(rsp->_statu);
        body += ": ";
        body += Util::StatuDesc(rsp->_statu);
        body += "</h1>";
        body += "</body>";
        body += "</html>";

        // 将页面数据，当作响应正文，放入rsp 的正文
        rsp->SetContent(body, "text/html");
    }

    void OnConnected(const PtrConnection &conn)      // 设置该 Connection 连接的上下文，建立好连接，先设置了上下文，OnMessage 里才能获取到
    {
        conn->SetContext(HttpContext());
        DEBUG_LOG("HttpServer::OnConnected, New Connection: %p  Has SetContext !", conn.get());
    }

    void OnMessage(const PtrConnection &conn, Buffer *buf)        // 解析缓冲区的数据 + 处理；；这个函数要设置给 TcpServer 的业务回调
    {
        while(buf->ReadableSize() > 0)
        {

            // 1、获取该连接的 http 上下文
            HttpContext *context = conn->GetContext()->get<HttpContext>();

            // 2、根据上下文，对该连接的 缓冲区数据进行解析，填充 HttpRequest 对象
            context->RecvHttpRequest(buf);
            HttpResponse rsp(context->RespStatu()); // 要用 状态码 初始化一下，以防 错误响应时，状态码还是 200

            HttpRequest &req = context->Request();

            if(context->RecvStatu() >= 400)     // 接收出错了
            {
                // 进行错误响应，关闭连接
                ErrorHandler(req, &rsp);     // 填充一个错误显示页面数据到rsp中
                WriteResponse(conn, req, &rsp);      // 组织响应并发送给客户端
                context->ReSet();
                conn->ShutDown();
                return;
            }

            if(context->RecvStatu() != RECV_HTTP_OVER)
            {
                // 当前请求还没有接收完整，退出，等待新数据到来再重新处理
                return;
            }

            // 3、对请求进行路由表的查找，执行查找到的方法（业务处理）
            Route(req, &rsp);

            // 4、处理完之后，组织 HttpResponse，进行发送
            WriteResponse(conn, req, &rsp);

            // 重置上下文
            context->ReSet();
            
            // 5、判断该连接是否长短连接，决定是否关闭连接
            if(rsp.Close() == true) conn->ShutDown();

        }
        return;
    }

public:
    HttpServer(uint16_t port, int timeout = DEFALT_TIMEOUT)
        :_server(port)
    {
        _server.EnableInactiveRelease(timeout);     // http 服务器应默认开启这种保护机制
        _server.SetConnectedCallback(std::bind(&HttpServer::OnConnected, this, std::placeholders::_1));
        _server.SetMessageCallback(std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
    }

    void SetBaseDir(const std::string &path)
    {
        assert(Util::IsDir(path));
        _basedir = path;
    }

    void Get(const std::string &pattern, const Handler &handler)      // 查找 Get 的路由表
    {
        _get_route.push_back(std::make_pair(std::regex(pattern), handler));
    }

    void Post(const std::string &pattern, const Handler &handler)     // 查找 Post 的路由表
    {
        _post_route.push_back(std::make_pair(std::regex(pattern), handler));
    }

    void Put(const std::string &pattern, const Handler &handler)     // 查找 Put 的路由表
    {
        _put_route.push_back(std::make_pair(std::regex(pattern), handler));
    }

    void Delete(const std::string &pattern, const Handler &handler)     // 查找 Delete 的路由表
    {
        _delete_route.push_back(std::make_pair(std::regex(pattern), handler));
    }

    void SetThreadCount(int count)      // TcpServer 中的从属线程数量
    {
        _server.SetThreadCount(count);
    }

    void Listen()   // 服务器启动
    {
        _server.Start();
    }

};



#endif